package cn.addr.user.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.exception.ConstraintViolationException;

@SuppressWarnings("unchecked")
public abstract class DaoBase<T> {
	// CONSTRUCT
    protected HibernateSessionFactoryWrap factory;

    /** shortcut
     */
    protected ThreadLocal<Session> thisSession;

    public DaoBase()
    {
        this.factory = HibernateSessionFactoryWrap.getInstance();
        this.thisSession = this.factory.getThreadLocal();

        return;
    }

    public DaoBase(HibernateSessionFactoryWrap factory)
    {
        this.factory = factory;
        this.thisSession = this.factory.getThreadLocal();

        return;
    }

  // TRANSACTION
    /** begin a session and transaction
     * Both session and xact are cross-dao, aka. once you invoke CatDao.begin(),
     * it is no needed to invoke DogDao.begin() in the same thread.
     */
    public void begin()
    {
        Session s;

        if (!(s=this.thisSession.get()).isOpen())
            this.thisSession.set(s=this.factory.openSession());
        if (!s.getTransaction().isActive())
            s.beginTransaction();

        return;
    }
    /** flush cached entities to disk.
     * Since session and xact are cross-dao, CatDao.flush() will also flush Dogs.
     */
    public void flush()
    {
        this.thisSession.get()
            .flush();

        return;
    }

    /** commit transaction
     * Since session and xact are cross-dao, It's no need to call commit() on every Dao.
     */
    public void commit()
    {
        this.thisSession.get()
            .getTransaction()
            .commit();

        return;
    }

    /** rollback transaction
     * Since session and xact are cross-dao, DogDao.rollback() will also rollback Cats.
     */
    public void rollback()
    {
        this.thisSession.get()
            .getTransaction()
            .rollback();

        return;
    }

    /** close session
     * Since session and xact are cross-dao, DogDao.rollback() will also rollback Cats.
     * Uncommited xact will be automatically rolled-back on session close.
     */
    public void close()
    {
        this.thisSession.get()
            .close();
        this.thisSession.remove();

        return;
    }

  // CRUD
    @SuppressWarnings("rawtypes")
	protected abstract Class classOfT();

    // NOT DELEGATED
    //public T create()
    //{
    //}

	public T get(Serializable id)
    {
        return(
            (T)(
                this.thisSession.get()
                    .get(this.classOfT(), id)
            )
        );
    }

    public T get(DetachedCriteria dc)
    {
        return(
            (T)(
                dc.getExecutableCriteria(
                    this.thisSession.get()
                ).uniqueResult()
        ));
    }

    @SuppressWarnings("rawtypes")
	public Object get(Class c, Serializable id)
    {
        return(
            this.thisSession.get()
                .get(c, id)
        );
    }

    public void lock(Object o)
    {
        this.thisSession.get()
            .buildLockRequest(LockOptions.UPGRADE).lock(o);
    }

    public Serializable save(Object o)
    {
        try
        {
            return(
                this.thisSession.get()
                    .save(o)
            );
        }
        catch (ConstraintViolationException ex)
        {
            try {
				throw(new Exception(ex.getMessage(), ex));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
		return null;
    }

    public void update(Object o)
    {
        this.thisSession.get()
            .update(o);

        return;
    }

    public void saveOrUpdate(Object o)
    {
        this.thisSession.get()
            .saveOrUpdate(o);

        return;
    }

    public void delete(Object o)
    {
        try
        {
            this.thisSession.get()
                .delete(o);
        }
        catch (ConstraintViolationException ex)
        {
            try {
				throw(new Exception(ex.getMessage(), ex));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }

        return;
    }

  // EXTENDED
    public Long count(DetachedCriteria dc)
    {
        Criteria c = dc.getExecutableCriteria(
                this.thisSession.get()
            ).setProjection(
                Projections.rowCount()
            );

        return((Long)c.uniqueResult());
    }

    @SuppressWarnings("rawtypes")
	public List search(DetachedCriteria dc, Integer start, Integer size)
    {
        Criteria c = dc.getExecutableCriteria(
            this.thisSession.get()
        );

        if (start != null)
            c.setFirstResult(start);
        if (size != null)
            c.setMaxResults(size);

        return(
            c.list()
        );
    }

    @SuppressWarnings("rawtypes")
	public List search(DetachedCriteria dc)
    {
        return(
            this.search(dc, null, null)
        );
    }

    public Query createQuery(String hql)
    {
        return(
            this.thisSession.get()
                .createQuery(hql)
        );
    }

  // MISC
    public Session getThisSession()
    {
        return(
            this.thisSession.get()
        );
    }
//	private static SessionFactory factory = null;
//	protected Session session = null;
//	protected Transaction tx = null;
//	static{
//		Configuration cfg = new Configuration().configure();
//		factory = cfg.buildSessionFactory();
//	}
//	
//	public void start(){
//		session = factory.openSession();
//		Transaction tx = session.beginTransaction();
//	}
//	
//	public void close(){
//		if(tx!=null)
//			tx.commit();
//		if(session!=null){
//			session.close();
//		}
//	}
//	
//	public void rollback(){
//		if(tx.isActive())
//			tx.rollback();
//	}
//	
//	public void save(T o){
//		session.save(o);
//	}
//	
//	public T findById(Long id){
//		T result = (T) session.get(classOf(),id);
//		return result;
//	}
//	
//	public List<T> getAll(){
//		String classname = classOf().getSimpleName();
//		String hql = "FROM "+classname;
//		List<T> result = session.createQuery(hql).list();
//		return result;
//	}
//	
//	public abstract Class classOf();
}
